package comlib

import chisel3._
import chisel3.util.log2Up

import comlib.util.{group, Fill0, Fill1, FillExt, FillMode}

class ChiselSeq[T<:Data] ( s:Seq[T] ) {

  /** Reduce process for Hardware binary-tree implementation
    *
    * @param f reduce process function
    * @param lockStage Register lock every `lockStage` level in binary-tree
    * @return A tuple (reduce value with T type, register lock number which reduce process consume)
    */
  def reduceTree( f: (T,T)=>T, lockStage: Int = 0, stage:Int = 1, slotSize:Int=0, fillVal:Option[T]=None ) = {
    val rdc = new ReduceProcess( s )
    rdc.setBinaryTree( lockStage, stage )
    rdc.setSerialize( slotSize, fillVal )
    val ret = rdc.serialBinaryTree( f )
    val ffs = rdc.delay
    (ret, ffs)
  }

  /** Group and translate them into Vec[Vec[T]]
    *
    * @param size Group size (1-N)
    * @param default Fill value when s.size cannot fill N*size
    *
    * @note When s.size%size != 0, defaul must be applied 
    */
  def groupVec( size:Int, default:T ) = {
    require( size > 0, s"Group size must be large than zero" )
    require( s.head.asUInt.getWidth == default.asUInt.getWidth, s"Parameter `default` must have same size with `s` element" )

    val ss = (s.size % size match {
      case 0 => s
      case r => s ++ Seq.fill(size-r)(default)
    }).map( _.asTypeOf(UInt()) ).map( _.asTypeOf(s.last) )   // 不知道为啥，只能这么操作 :-)
    VecInit(ss.grouped(size).map{ case e => VecInit(e) }.toSeq)
    
  }

  /** Group and translate them into Vec[Vec[T]]
    *
    * @param size Group size (1-N)
    *
    */
  def groupVec( size:Int ) = {
    require( size > 0, s"Group size must be large than zero" )
    require( s.size % size == 0, s"Please use another [[groupVec]] version(Provide `default` parameter version)" )
    VecInit(s.grouped(size).map{ case e => VecInit(e) }.toSeq)
  }

}

class ComlibData(val d:Data) {
  def group[A<:Data](gen:A, fill:FillMode):Vec[A] = comlib.util.group(gen,d,fill)
}
